'''
This is TwistedSim script that shows the user how to emulate 
our simple-torrent application under TwistedSim.
'''


from twistedSim.simManager import SimManager, Machine, Module, IpInterface
from twistedSim.moduleHandler import twistedH

import os
from os import path
import shutil
import struct
from random import randrange

#The following assigned values are the default assigned values if the user didn't
#specify in the value of the parameter in sys.argv.
#Change these value to change default values.
torrentName = "Lawrence_ext.tor"
sharedFileName = "Camel - Lawrence.flv"
seederStartTime = 10

numberOfLeechers = 25
leecherStartTime = 60 #The start time for first leechers
leecherTimeDelta = 120 #The difference between two leechers
peersDeathInterval = 10*60  #Death cycle interval
peesrDeathRatio = 10.00/100  #Death ratio of peers per cycle

scriptStopTime = 1*60*60#The simulation stop time from the time of the last
                        # leecher is created in.

#The following are global data for internal use, bad design: should not
# use globals.
leechers = [] #All alive leechers will be kept here,
trackerFiles = ["__init__.py", "tracker.py", "util.py"] #copy files to directory
#Both of the next 2 lists are reassigned in parseArgs()
leecherFiles = ["__init__.py", "peer.py", "torrentFile.py",
                "peerToTracker.py", "util.py"]# + torrentName] #not assigned yet
seederFiles = leecherFiles #+ [sharedFileName] #sharedFileName not assigned yet


def parseArgs():
    global torrentName, sharedFileName, numberOfLeechers
    global leecherFiles, seederFiles, peersDeathInterval, peesrDeathRatio
    global seederStartTime, leecherStartTime, leecherTimeDelta
    from sys import argv
    from twisted.python import usage
    
    class ParseOptions(usage.Options):
        optParameters = [
     ["torrent", "t", torrentName, "The torrent file name"],
     ["fileShared", "f", sharedFileName, "Name of the file to be shared"],
     ["nLeechers", "n", numberOfLeechers, "Number of leechers to be created", int],
     ["seederTime", "s", seederStartTime, "The start time (from simulation start) of the only seeder", float],
     ["leecherTime", "l", leecherStartTime, "The start time (from simulation start) of the first leecher", float],
     ["leecherInterval", "i", leecherTimeDelta,"Time interval between two created leechers", float],
     ["deathInterval","d",peersDeathInterval, "The time interval between two death cycles", float],
     ["deathRatio", "r" , peesrDeathRatio,"The ratio of the alive peers that will be killed per death cycle", float]]
    opts = ParseOptions()
    opts.parseOptions()
    if len(argv) == 1:
        print "Script: Using default values, for help type:"
        print argv[0] + " --help"
    else:
        torrentName = opts.opts["torrent"]
        sharedFileName = opts.opts["fileShared"]
        numberOfLeechers = opts.opts["nLeechers"]
        seederStartTime = opts.opts["seederTime"]
        leecherStartTime = opts.opts["leecherTime"]
        leecherTimeDelta = opts.opts["leecherInterval"]
        peersDeathInterval = opts.opts["deathInterval"]
        peersDeathRatio = opts.opts["deathRatio"]              
    leecherFiles = leecherFiles + [torrentName]
    seederFiles = leecherFiles + [sharedFileName]


def createTracker():
    trackerName = "Tracker"
    trackerPath = createPeerDir(trackerName, trackerFiles)
    tracker = Module("tracker", twistedH(), trackerPath,
                      startTime = 0, nickName = trackerName)
    trackerMachine = Machine()
    trackerIP = IpInterface("34.131.24.213", trackerMachine)
    SimManager.addModule(tracker, trackerMachine)
    
def createSeeder():
    seederName = "Seeder"
    seederPath = createPeerDir(seederName, seederFiles)
    seeder = Module("peer", twistedH(), seederPath, 
                    sysArgs = torrentName, startTime = seederStartTime,
                    nickName = seederName)
    seederMachine = Machine()
    seederIP = IpInterface("21.214.9.1", seederMachine)
    SimManager.addModule(seeder, seederMachine)

def createPeerDir(peerName, listOfFiles):
    os.mkdir(peerName)
    peerPath =  path.realpath(".") + "/" + peerName
    map(shutil.copy, listOfFiles, [peerPath]*len(listOfFiles))
    return peerPath

def createLeechers():
    #Timing stuff:
    global leecherStartTime, leecherTimeDelta, leechers
    #IP Stuff:
    ipPrefix = "121"
    ipSecondPart = 7 
    ipThirdPart = 31
    ipFourthPart = 70
    
    for leecherCount in range(numberOfLeechers):
        leecherName = "Leecher" + str(leecherCount)
        #Create leecher's directory:
        leecherPath = createPeerDir(leecherName, leecherFiles)
        #Create leecher's module:
        leecher = Module("peer", twistedH(), leecherPath, 
                         sysArgs = torrentName, 
                         startTime = leecherStartTime+ leecherTimeDelta,
                         nickName = leecherName)
        leechers.append(leecher)
        leecherStartTime += leecherTimeDelta
        #Create machine:
        leecherMachine = Machine()
        SimManager.addModule(leecher, leecherMachine)
        #Create IP interface:
        ipStr = map(lambda intIp: str(intIp),[ipSecondPart, ipThirdPart,
                                               ipFourthPart]) #Convert int to str
        ip = str.join(".", ([ipPrefix] + ipStr)) #Create the actual ip
        leecherIP = IpInterface(ip,leecherMachine)
        if ipFourthPart == 255:
            ipFourthPart = 0
            if ipThirdPart == 255:
                ipSecondPart += 1
                ipThirdPart = 0
            else:
                ipThirdPart += 1 
        else:
            ipFourthPart += 1

def randomDeath():
    def serialKiller():
        aliveNodes = filter(lambda x:x.isRunning(), leechers)
        numOfDeaths = int(len(aliveNodes) * peesrDeathRatio)
        #print "Number of deaths: " , numOfDeaths, "\tAlive: ", len(aliveNodes)
        for i in range(numOfDeaths):
            foundAliveVictim = False
            while not foundAliveVictim:
                victimIndex = randrange(len(leechers))
                unluckyPeer = leechers[victimIndex]
                if unluckyPeer.isRunning():
                    foundAliveVictim = True
                    leechers.pop(victimIndex)
            unluckyPeer.die()
    #Avoid killing on our first run, the simulation might haven't yet
    # started
    SimManager.callLater(peersDeathInterval, serialKiller)
    SimManager.callLater(peersDeathInterval, randomDeath)

def setStopTime():
    baseStopTime = leecherStartTime + (leecherTimeDelta*numberOfLeechers)
    stopTime = baseStopTime + scriptStopTime
    def stopSim():
        SimManager.stop = True
    SimManager.callLater(stopTime, stopSim)


def trackTime():
    import time
    from twistedSim.overrideManager import OverrideManager
    realAscTime = OverrideManager.getEntity("time.asctime")
    realTime = OverrideManager.getEntity("time.time")
    startTime = time.time()
    def concludeTime():
        print "SimpleTorrent: " 
        print "Peers: ", numberOfLeechers
        fullPath = os.path.realpath(".") + "/" + sharedFileName
        normedPath = os.path.normpath(fullPath)
        print "FileSizeKb: ", os.path.getsize(normedPath)/1024.0
        print "Leecher Interval: ", leecherTimeDelta
        print "Death Interval: ", peersDeathInterval
        print "Death Ratio: ", peesrDeathRatio
        print "Time now: " + time.asctime()
        print "Time since i started: " + str(time.time() - startTime)
        print "Real TIme: " + realAscTime()
        print "Real time since i started: " + str(realTime() - startTime)
        print "Simulated - Real = " + str(time.time() - realTime())    
    SimManager.addPostRun(concludeTime)



def main():
    parseArgs()
    createTracker()
    createSeeder()
    createLeechers()
    randomDeath()
    setStopTime()
    trackTime()
    SimManager.run()
    
    
if __name__ == '__main__':
    main()
